package com.xindacloud.sms.adapter.service.sms.impl;

import com.google.gson.Gson;
import com.xindacloud.sms.adapter.bo.DomesticSmsReportRequestBodyBO;
import com.xindacloud.sms.adapter.bo.DomesticSmsRequestBodyBO;
import com.xindacloud.sms.adapter.bo.DomesticSmsResponseBO;
import com.xindacloud.sms.adapter.bo.SubmitMessageBO;
import com.xindacloud.sms.adapter.exception.ServiceException;
import com.xindacloud.sms.adapter.service.sms.ISubmitMessageService;
import com.xindacloud.sms.adapter.utils.DateHelp;
import com.xindacloud.sms.adapter.utils.UnionResultHelp;
import com.xindacloud.sms.adapter.vo.SubmitMessageResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

@Service("domesticSubmitMessageService")
@Slf4j
public class DomesticSubmitMessageServiceImpl implements ISubmitMessageService<SubmitMessageBO, SubmitMessageResultVO> {

    @Override
    public SubmitMessageResultVO submitMessage(SubmitMessageBO submitMessageBO) {

        final String apiKey = submitMessageBO.getApiKey();
        final String apiSecret = submitMessageBO.getApiSecret();
        final String apiUrl = submitMessageBO.getApiUrl();
        final String callBackUrl = submitMessageBO.getCallBackUrl();
        final String content = submitMessageBO.getMessageBody();
        final String recipient = submitMessageBO.getRecipient();

        final String nonce = UUID.randomUUID().toString();
        final String timestamp = DateHelp.localDateTime2Str(LocalDateTime.now());

        try {
            final DomesticSmsRequestBodyBO bodyEntity = DomesticSmsRequestBodyBO.builder()
                    .access_key(apiKey)
                    .content(content)
                    .dest_number(recipient)
                    .nonce(nonce)
                    .signature("hexindacloud")
                    .timestamp(timestamp)
                    .build();
            final String requestBody = new Gson().toJson(bodyEntity);
            String accessToken = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, apiSecret)
                    .hmacHex(requestBody);
            DomesticSmsResponseBO domesticSmsResponseBO = WebClient
                    .create(apiUrl)
                    .post()
                    .accept(MediaType.APPLICATION_JSON)
                    .headers(httpHeaders -> {
                        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                        httpHeaders.set("Access-Token", accessToken);
                    })
                    .body(BodyInserters.fromObject(requestBody))
                    .retrieve()
                    .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                            Mono.error(new ServiceException("国内通道提交短信异常.", clientResponse.statusCode().value()))
                    )
                    .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                            Mono.error(new ServiceException("国内通道提交短信异常.", clientResponse.statusCode().value()))
                    )
                    .bodyToMono(new ParameterizedTypeReference<DomesticSmsResponseBO>() {
                    })
                    .block();
            String uuid = Objects.nonNull(domesticSmsResponseBO) ? domesticSmsResponseBO.getUuid() : "";
            Integer code = domesticSmsResponseBO.getCode();
            //回调报告
            CompletableFuture.supplyAsync(() -> callBackReport(callBackUrl, content, recipient, nonce, timestamp, accessToken, uuid, code));
            return SubmitMessageResultVO.builder()
                    .submitStatus(0)
                    .network("")
                    .messageId(uuid)
                    .errorText(domesticSmsResponseBO.getMessage())
                    .build();

        } catch (Exception e) {
            log.error("国内发送短信异常:{}", e.getMessage(), e);
            throw new ServiceException("短信发送异常".concat(e.getMessage()), UnionResultHelp.StatusEnum.BadRequest.code());
        }
    }

    private UnionResultHelp callBackReport(String callBackUrl, String content, String recipient, String nonce, String timestamp, String accessToken, String uuid, Integer code) {
        final DomesticSmsReportRequestBodyBO reportRequestBodyBO = DomesticSmsReportRequestBodyBO.builder()
                .timestamp(timestamp)
                .nonce(nonce)
                .report_time(timestamp)
                .uuid(uuid)
                .dest_number(recipient)
                .sms_seq(content.length())
                .sms_size(1)
                .success(Objects.equals(code, 0))
                .error_code(code.toString())
                .build();

        WebClient
                .create(callBackUrl)
                .post()
                .accept(MediaType.APPLICATION_JSON)
                .headers(httpHeaders -> {
                    httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                    httpHeaders.set("Access-Token", accessToken);
                })
                .body(Mono.just(reportRequestBodyBO), DomesticSmsReportRequestBodyBO.class)
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                        Mono.error(new ServiceException("推送报告异常.", clientResponse.statusCode().value()))
                )
                .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                        Mono.error(new ServiceException("推送报告异常.", clientResponse.statusCode().value()))
                )
                .bodyToMono(new ParameterizedTypeReference<UnionResultHelp>() {
                })
                .block();
        return new UnionResultHelp(UnionResultHelp.StatusEnum.Success, "Success");
    }
}
